package core.proto.vis.action;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import javax.swing.DefaultListModel;

import prefuse.Visualization;
import prefuse.action.GroupAction;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.tuple.TupleSet;
import prefuse.visual.EdgeItem;
import prefuse.visual.NodeItem;
import core.proto.vis.VizDemoRadial;
import core.proto.vis.action.util.Position;
import core.util.logging.SimpleLogger;
import core.util.logging.constants.Location;

/**
 * Switch the root of the tree by requesting a new spanning tree at the desired
 * root
 * 
 * @author Bill
 */
public class TreeRootAction extends GroupAction
{
    private static final SimpleLogger LOGGER = SimpleLogger.getLogger();

    private Map<NodeItem, Position> positions;

    private Map<String, List<EdgeItem>> attrMapping;

    private List<EdgeItem> lastHighlighted;

    private DefaultListModel listData;

    public TreeRootAction( String graphGroup,
            Map<NodeItem, Position> positions, DefaultListModel listData,
            Map<String, List<EdgeItem>> attrMapping ) {
        super( graphGroup );
        this.positions = positions;
        this.listData = listData;
        this.attrMapping = attrMapping;
        this.lastHighlighted = lastHighlighted;
    }

    public void run( double frac ) {
        attrMapping.clear();

        // set every edge to unhighlighted
        TupleSet edges = m_vis.getGroup( "tree.edges" );
        Iterator it = edges.tuples();
        while( it.hasNext() ) {
            Object next = it.next();
            if( next instanceof EdgeItem ) {
                ((EdgeItem) next).setHighlighted( false );
            }
        }

        TupleSet focus = m_vis.getGroup( Visualization.FOCUS_ITEMS );
        if( focus == null || focus.getTupleCount() == 0 )
            return;

        Graph g = (Graph) m_vis.getGroup( m_group );

        this.listData.clear();

        Node f = null;
        Object o = null;
        @SuppressWarnings("rawtypes")
        Iterator tuples = focus.tuples();
        while( tuples.hasNext() ) {
            o = tuples.next();
            // if o is not a node or it's not part of the graph
            if( !(o instanceof Node) || !g.containsTuple( f = (Node) o ) ) {
                return;
            }
            LOGGER.log( "Node: " + f.get( "name" ), Location.VZ,
                    "node-click: recenter graph" );
            g.getSpanningTree( f );

            // re-locate the locked nodes
            for( NodeItem n : positions.keySet() ) {
                Position p = positions.get( n );
                n.setX( p.getX() );
                n.setY( p.getY() );
            }

            // we want all of the attributes, non-dupe
            Set<String> attributes = new HashSet<String>();
            String attribute;

            TupleSet edgeSet = m_vis.getGroup( VizDemoRadial.BLUE_EDGE_SET );
            Iterator inIter = f.inEdges();
            Iterator outIter = f.outEdges();
            edgeSet.clear();
            while( inIter.hasNext() ) {
                EdgeItem edge = (EdgeItem) inIter.next();
                edgeSet.addTuple( edge );
                updateAttributeSet( attributes, attrMapping, edge );
            }
            while( outIter.hasNext() ) {
                EdgeItem edge = (EdgeItem) outIter.next();
                edgeSet.addTuple( edge );
                updateAttributeSet( attributes, attrMapping, edge );
            }

            Iterator nodeIter = f.inNeighbors();
            while( nodeIter.hasNext() ) {
                NodeItem n = (NodeItem) nodeIter.next();
                n.setHighlighted( true );
            }

            for( String attr : attributes ) {
                listData.addElement( attr );
            }
        }
    }

    private static void updateAttributeSet( Set<String> attributes,
            Map<String, List<EdgeItem>> attrMapping, EdgeItem edge ) {
        String attribute;
        if( edge.canGetString( "name" ) ) {
            attribute = edge.getString( "name" );
            StringTokenizer st = new StringTokenizer( attribute, ", \t\n" );
            while( st.hasMoreElements() ) {
                String attr = st.nextElement().toString().trim();
                attributes.add( attr );
                if( attrMapping.containsKey( attr ) ) {
                    attrMapping.get( attr ).add( edge );
                } else {
                    List<EdgeItem> edges = new ArrayList<EdgeItem>();
                    edges.add( edge );
                    attrMapping.put( attr, edges );
                }
            }
        }
    }
}
